﻿namespace EAL.ViewModels
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;
    using EAL.Database;
    using EAL.Models;
    using EAL.MVVM;

    public class OrdersVM : TabItemVM
    {
        private Dictionary<Position, bool> _positionValidationResultLookUp = new Dictionary<Position, bool>();

        /// <summary>
        /// database context
        /// </summary>
        private DbContext _dbContext;
        public DbContext DbContext
        {
            get { return _dbContext; }
            set
            {
                _dbContext = value;

                //in runtime mode we need to load the database rows when context is set
                if (!DesignerProperties.GetIsInDesignMode(new FrameworkElement()))
                    this._InitDataDatabase();

                _dbContext.PreviewOnSaveChangesEvent += new DbContext.SaveChangesEventHandler(_dbContext_PreviewOnSaveChangesEventHandler);
            }
        }

        void _dbContext_PreviewOnSaveChangesEventHandler(object sender, SaveChangesEventArgs e)
        {
            var itemsAddedCustomer = e.Tracker.Entries<Customer>().Where(c => c.State == System.Data.EntityState.Added);
            foreach (var item in itemsAddedCustomer)
            {
                this._customerLookup.Add(item.Entity);
                this.OnPropertyChanged(() => this.CustomerLookup);
            }

            var itemsAddedProduct = e.Tracker.Entries<Product>().Where(c => c.State == System.Data.EntityState.Added);
            foreach (var item in itemsAddedProduct)
            {
                this._productLookup.Add(item.Entity);
                this.OnPropertyChanged(() => this.ProductLookup);
            }

            var itemsDeletedCustomer = e.Tracker.Entries<Customer>().Where(c => c.State == System.Data.EntityState.Deleted);
            foreach (var item in itemsDeletedCustomer)
            {
                this._customerLookup.Remove(item.Entity);
                this.OnPropertyChanged(() => this.CustomerLookup);
            }
            var itemsDeletedProduct = e.Tracker.Entries<Product>().Where(c => c.State == System.Data.EntityState.Deleted);
            foreach (var item in itemsDeletedProduct)
            {
                this._productLookup.Remove(item.Entity);
            }
        }

        /// <summary>
        /// collection of orders
        /// </summary>
        private OrderList _orders;
        public OrderList Orders
        {
            get { return _orders; }
            set { _orders = value; }
        }

        /// <summary>
        /// customer lookup list
        /// </summary>
        private ObservableCollection<Customer> _customerLookup;
        public ObservableCollection<Customer> CustomerLookup
        {
            get { return _customerLookup; }
            set { 
                _customerLookup = value;
                this.OnPropertyChanged(() => this.CustomerLookup);
            }
        }

        /// <summary>
        /// product lookup list
        /// </summary>
        private ObservableCollection<Product> _productLookup;
        public ObservableCollection<Product> ProductLookup
        {
            get { return _productLookup; }
            set {
                _productLookup = value;

                this.OnPropertyChanged(() => this.ProductLookup);
            }
        }

        /// <summary>
        /// order state lookup list
        /// </summary>
        private List<String> _orderStateLookup;
        public List<String> OrderStateLookup
        {
            get { return _orderStateLookup; }
            set { _orderStateLookup = value; }
        }

        /// <summary>
        /// selected order to bind to the view
        /// (can and should be synchronized with the selected item of item controls)
        /// </summary>
        private Order _selectedOrder = null;
        public Order SelectedOrder
        {
            get 
            { 
                return _selectedOrder; 
            }
            set
            {
                _selectedOrder = value;
                NewOrderCommand.RaiseCanExecuteChanged();
                DeleteOrderCommand.RaiseCanExecuteChanged();
                NewOrderDetailCommand.RaiseCanExecuteChanged();

                //get default view of the order details collection related to this order and save it
//                if (_selectedOrder != null)
//                    this._orderDetailView = (BindingListCollectionView)CollectionViewSource.GetDefaultView(this._selectedOrder.Positions);
            }
        }

        /// <summary>
        /// selected order detail to bind to the view
        /// (can and should be synchronized with the selected item of item controls)
        /// </summary>
        private Position _selectedOrderDetail = null;
        public Position SelectedOrderDetail
        {
            get 
            { 
                return _selectedOrderDetail; 
            }
            set
            {
                _selectedOrderDetail = value;

                if (null != _selectedOrderDetail)
                    //_selectedOrderDetail.PropertyChanged += new PropertyChangedEventHandler(_selectedOrderDetail_PropertyChanged);

                DeleteOrderDetailCommand.RaiseCanExecuteChanged();
            }
        }

        //private bool _propertyChangedCalledAgain = false;
        //void _selectedOrderDetail_PropertyChanged(object sender, PropertyChangedEventArgs e)
        //{
        //    if (!_propertyChangedCalledAgain)
        //    {
        //        _propertyChangedCalledAgain = true;
        //        var orderDetail = sender as Position;

        //        if (null != orderDetail)
        //        {
        //            if (null != orderDetail.Product)
        //            {
        //                var product = (from p in this._dbContext.Products
        //                               where p.Id == orderDetail.Product.Id
        //                               select p).FirstOrDefault();

        //                if (null == product || product.Stock < orderDetail.Amount)
        //                    orderDetail.Amount = 0;
        //            }
        //            else
        //                orderDetail.Amount = 0;
        //        }

        //        _propertyChangedCalledAgain = false;
        //    }
        //}

        /// <summary>
        /// command used to add a new order, can be bound to buttons
        /// </summary>
        private RelayCommand _newOrderCommand;
        public RelayCommand NewOrderCommand
        {
            get
            {
                if (this._newOrderCommand == null)
                    this._newOrderCommand = new RelayCommand(this._AddNewOrder);

                return _newOrderCommand;
            }
        }

        /// <summary>
        /// command used to delete an existing order and its order details, can be bound to buttons
        /// </summary>
        private RelayCommand _deleteOrderCommand;
        public RelayCommand DeleteOrderCommand
        {
            get
            {
                if (this._deleteOrderCommand == null)
                {
                    this._deleteOrderCommand = new RelayCommand(this._DeleteOrder,
                        o => this._selectedOrder != null);
                }

                return _deleteOrderCommand;
            }
        }

        /// <summary>
        /// command used to save the order and order details data to the database context, can be bound to buttons
        /// </summary>
        private RelayCommand _saveCommand;
        public RelayCommand SaveCommand
        {
            get
            {
                if (_saveCommand == null)
                    this._saveCommand = new RelayCommand(this._SaveChangesToDB, (o) => this._positionValidationResultLookUp.All<KeyValuePair<Position, bool>>(x => x.Value == true));

                return _saveCommand;
            }
        }

        /// <summary>
        /// command used to add a new order detail, can be bound to buttons
        /// </summary>
        private RelayCommand _newOrderDetailCommand;
        public RelayCommand NewOrderDetailCommand
        {
            get
            {
                if (this._newOrderDetailCommand == null)
                    this._newOrderDetailCommand = new RelayCommand(this._AddNewOrderDetail,
                        o => this._selectedOrder != null);

                return _newOrderDetailCommand;
            }
        }

        /// <summary>
        /// command used to delete an existing order detail, can be bound to buttons
        /// </summary>
        private RelayCommand _deleteOrderDetailCommand;
        public RelayCommand DeleteOrderDetailCommand
        {
            get
            {
                if (this._deleteOrderDetailCommand == null)
                {
                    this._deleteOrderDetailCommand = new RelayCommand(this._DeleteOrderDetail,
                        o => this._selectedOrderDetail != null);
                }

                return _deleteOrderDetailCommand;
            }
        }

        /// <summary>
        /// view related to the order collection
        /// </summary>
        private ListCollectionView _orderView;

        /// <summary>
        /// ctor
        /// </summary>
        public OrdersVM()
        {
            //in design mode we need to show some sample rows
            if (DesignerProperties.GetIsInDesignMode(new FrameworkElement()))
                _InitDataDesigner();
        }


        /// <summary>
        /// initializes data for design mode
        /// </summary>
        private void _InitDataDesigner()
        {
            List<Order> temp = new List<Order>();

            //Order tempOrder = new Order();
            //tempOrder.Id = 1;
            //tempOrder.CreationTime = DateTime.Now;
            //tempOrder.Customer = new Customer() { Address="blastr 36", FirstName="Martin", LastName="Kuschnik", Id=1 };
            //tempOrder.Positions = new EntityCollection<Position>();
            //tempOrder.Positions.Add(new Position() { Amount = 0, Id = 1, Product = new Product() { Id=1, Name="muhprodukt", Price=10.0m, Stock=20 } });
            //tempOrder.State = OrderState.Payed;

            //temp.Add(tempOrder);

            //create new order list and add sample data to it
            this._orders = new OrderList(temp, this.DbContext);
        }

        /// <summary>
        /// initializes data for runtime mode
        /// </summary>
        private void _InitDataDatabase()
        {
            //retrieve all orders from the database context and save it to the newly
            //created order collection
            this._orders = new OrderList(this.DbContext.Orders, this.DbContext);
            
            //get default view of the order collection and save it
            this._orderView = (ListCollectionView)CollectionViewSource.GetDefaultView(this._orders);

            //fill the product lookup
            this._productLookup = new ObservableCollection<Product>(this._dbContext.Products.ToList());

            //fill the customer lookup
            this._customerLookup = new ObservableCollection<Customer>(this._dbContext.Customers.ToList());

            //fill order state lookup
            this._orderStateLookup = new List<String>();
            this._orderStateLookup.Add("Erstellt");
            this._orderStateLookup.Add("In Bearbeitung");
            this._orderStateLookup.Add("Bezahlt");
            this._orderStateLookup.Add("An Kunde gesendet");
            this._orderStateLookup.Add("Abgeschlossen");
        }

        /// <summary>
        /// adds a new order to the view and its underlying collection
        /// </summary>
        /// <param name="parameter"></param>
        private void _AddNewOrder(object parameter)
        {
            this._orderView.AddNew();
            this._orderView.CommitNew();
            NewOrderDetailCommand.RaiseCanExecuteChanged();
        }

        /// <summary>
        /// deletes an order and its order details from the view and its underlying collection
        /// </summary>
        /// <param name="parameter"></param>
        private void _DeleteOrder(object parameter)
        {
            if (this._selectedOrder != null)
            {
                this._orderView.Remove(this._selectedOrder);
            }
        }

        /// <summary>
        /// saves the changed data of the order and order details collection to the database using the context
        /// </summary>
        /// <param name="paramter"></param>
        private void _SaveChangesToDB(object paramter)
        {
            this.DbContext.SaveChanges();
        }

        /// <summary>
        /// adds a new order detail to the view and its underlying collection
        /// </summary>
        /// <param name="parameter"></param>
        private void _AddNewOrderDetail(object parameter)
        {
            if (this._selectedOrder != null)// && this._orderDetailView != null)
            {
                Position p = new Position() { Amount= 1 };
                p.AmountChanged += new Action<Position>(AmountChanged);
                _selectedOrder.Positions.Add(p);
                //var orderDetailView = (ListCollectionView)CollectionViewSource.GetDefaultView(this._selectedOrder.Positions);
                //orderDetailView.AddNew();
                //orderDetailView.CommitNew();
                //orderDetailView.Refresh();
            }
        }

        private void AmountChanged(Position position)
        {
            if (position.Amount <= 0)
            {
                _positionValidationResultLookUp[position] = false;
                this.SaveCommand.RaiseCanExecuteChanged();
                throw new ValidationException("Ein Produkt muss mindestend 1 mal bestellt werden.");
            }

            if (position.Product == null)
            {                
                _positionValidationResultLookUp[position] = false;
                this.SaveCommand.RaiseCanExecuteChanged();
                throw new ValidationException("Kein Produkt gewählt.");
            }

            var product = (from p in this._dbContext.Products
                           where p.Id == position.Product.Id
                           select p).FirstOrDefault();

            if (null == product || product.Stock < position.Amount)
            {
                _positionValidationResultLookUp[position] = false;
                this.SaveCommand.RaiseCanExecuteChanged();
                throw new ValidationException("Anzahl zu groß.");
            }

            _positionValidationResultLookUp[position] = true;
            this.SaveCommand.RaiseCanExecuteChanged();
        }

        /// <summary>
        /// deletes an order detail from the view and its underlying collection
        /// </summary>
        /// <param name="parameter"></param>
        private void _DeleteOrderDetail(object parameter)
        {
            if (this._selectedOrderDetail != null)
            {
                int index = this._orders.IndexOf(this._selectedOrder);

                if (index >= 0)
                {
                    this._orders[index].Positions.Remove(this._selectedOrderDetail);
                    //this._orderDetailView.Remove(this._selectedOrderDetail);
                }
            }
            NewOrderDetailCommand.RaiseCanExecuteChanged();
            DeleteOrderDetailCommand.RaiseCanExecuteChanged();
        }
    }
}
